package com.mingyuean.demo.mybatis.builder.xml;

import com.mingyuean.demo.mybatis.builder.BaseBuilder;
import com.mingyuean.demo.mybatis.datasource.DataSourceFactory;
import com.mingyuean.demo.mybatis.io.Resources;
import com.mingyuean.demo.mybatis.mapping.BoundSql;
import com.mingyuean.demo.mybatis.mapping.Environment;
import com.mingyuean.demo.mybatis.mapping.MappedStatement;
import com.mingyuean.demo.mybatis.mapping.SqlCommandType;
import com.mingyuean.demo.mybatis.session.Configuration;
import com.mingyuean.demo.mybatis.transaction.TransactionFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author MingYueAn
 * <p>  XML 配置解析器，基于 dom4j 处理 xml 文件。
 * <p>  专门解析核心配置文件的解析类
 * <p>  2023/3/17 9:26
 * @version: 1.0
 */
public class XMLConfigBuilder extends BaseBuilder {
    private static final Logger log = LoggerFactory.getLogger(XMLConfigBuilder.class);

    /**
     * XML的根节点
     */
    private Element root;

    /**
     * 构造方法，通过读取 InputStream 对象解析 MyBatis 配置文件
     *
     * @param inputStream MyBatis 配置文件的 InputStream 对象
     */
    public XMLConfigBuilder(InputStream inputStream) {
        // 1. 调用父类初始化Configuration
        super(new Configuration());
        // 2. dom4j 处理 xml
        try {
            // 开始解析xml
            final Document document = new SAXReader().read(inputStream);
            // 获取xml文件的根节点
            root = document.getRootElement();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用dom4j解析配置文件，封装Configuration对象
     *
     * @return 全局配置，解析后的 Configuration 对象
     */
    public Configuration parse() {
        try {
            // TODO: 2023/3/20 未完待续
            // 解析环境
            environmentsElement(root.element("environments"));
            // 解析映射器
            mapperElement(root.element("mappers"));
        } catch (Exception e) {
            throw new RuntimeException("分析SQL配置时出错" + e, e);
        }
        return configuration;
    }

    /**
     * 解析环境
     *
     * @param environments
     */
    private void environmentsElement(Element environments) throws Exception {
        // 获取默认环境
        final String environment = environments.attributeValue("default");
        // 获取全部环境
        final List<Element> elementList = environments.elements("environment");
        for (Element e : elementList) {
            // 获取当前环境的id
            final String id = e.attributeValue("id");
            if (environment.equals(id)) {
                // 事务管理器
                final String transactionType = e.element("transactionManager").attributeValue("type");
                log.debug("事务管理器transactionType = {}", transactionType);
                // 获取事务工厂
                TransactionFactory txFactory =
                        (TransactionFactory) typeAliasRegistry
                                // 通过类型别名获取字节码对象
                                .resolveAlias(transactionType)
                                // 使用反射将字节码对象进行实例化
                                .newInstance();
                // 数据源
                final String dataSourceType = e.element("dataSource").attributeValue("type");
                log.debug("数据源dataSourceType = {}", dataSourceType);
                // 获取数据源工厂
                DataSourceFactory dataSourceFactory =
                        (DataSourceFactory) typeAliasRegistry
                                // 通过类型别名获取字节码对象
                                .resolveAlias(dataSourceType)
                                // 使用反射将字节码对象进行实例化
                                .newInstance();
                // 从 XML 配置文件中获取名为 "dataSource" 的子元素，并获取其下所有名为 "property" 的子元素列表
                final List<Element> propertyList = e.element("dataSource").elements("property");
                // 存储数据库连接属性的键值对
                final Properties properties = new Properties();
                // 遍历数据库连接属性列表，逐个将属性名和属性值存入 Properties 对象中
                for (Element property : propertyList) {
                    final String name = property.attributeValue("name");
                    final String value = property.attributeValue("value");
                    // 将属性名和属性值存入 Properties 对象中
                    properties.setProperty(name, value);
                }
                // 使用 DataSourceFactory 对象设置数据源的属性
                dataSourceFactory.setProperties(properties);
                // 获取数据源实例
                final DataSource dataSource = dataSourceFactory.getDataSource();
                // 构建环境
                final Environment environmentBuild = new Environment.Builder(id)
                        .transactionFactory(txFactory)
                        .dataSource(dataSource)
                        .build();
                configuration.setEnvironment(environmentBuild);
            }
        }
    }

    /**
     * 解析映射器
     *
     * @param mappers
     * @throws Exception
     */
    private void mapperElement(Element mappers) throws Exception {
        // 获取所有的 mapper 节点
        final List<Element> mapperList = mappers.elements("mapper");
        for (Element element : mapperList) {
            // 获取 mapper 节点中的 resource 属性，即 Mapper XML 文件的路径
            final String resource = element.attributeValue("resource");
            // 读取 Mapper XML 文件
            final InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            // 解析 Mapper XML 文件
            final Document document = new SAXReader().read(resourceAsStream);
            // 获取根节点
            final Element mapperRoot = document.getRootElement();
            // 获取命名空间
            final String namespace = mapperRoot.attributeValue("namespace");
            log.debug("命名空间 = {}", namespace);
            // 注册Mapper映射器
            configuration.addMapper(Resources.classForName(namespace));
            // TODO: 2023/3/20 未完待续
            // SELECT
            final List<Element> selectElementList = mapperRoot.elements("select");
            for (Element selectElement : selectElementList) {
                // 获取 SQL 语句的 ID、参数类型、返回类型和文本内容
                final String id = selectElement.attributeValue("id");
                // 参数类型
                final String parameterType = selectElement.attributeValue("parameterType");
                // 返回类型
                final String resultType = selectElement.attributeValue("resultType");
                // 文本内容：前sql
                String sql = selectElement.getText();
                // 解析 SQL 语句
                final Map<Integer, String> parameterMap = new HashMap<>();
                final Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                final Matcher matcher = pattern.matcher(sql);
                for (int i = 0; matcher.find(); i++) {
                    final String group1 = matcher.group(1);
                    final String group2 = matcher.group(2);
                    parameterMap.put(i, group2);
                    sql = sql.replace(group1, "?");
                }
                // 解析完成
                log.debug("后sql = {}", sql);
                // SQL语句的标识：namespace + "." + id
                final String namespaceId = namespace + "." + id;
                log.debug("SQL语句的标识 namespaceId = {}", namespaceId);
                // sql命令类型
                final String selectName = selectElement.getName();
                final SqlCommandType sqlCommandType = SqlCommandType.valueOf(selectName.toUpperCase(Locale.ENGLISH));
                log.debug("sql命令类型 = {}", sqlCommandType);

                final BoundSql boundSql = new BoundSql(resultType, parameterType, parameterMap, sql);
                // 构建 MappedStatement 对象
                MappedStatement mappedStatement = new MappedStatement
                        .Builder(configuration, namespaceId, sqlCommandType, boundSql)
                        .build();
                // 将 MappedStatement 对象添加到 Configuration 中
                configuration.addMappedStatement(mappedStatement);
            }
        }
    }
}
